package com.api.apigateway.risk;

import com.api.apigateway.filter.ApiGlobalFilter;
import com.api.common.manager.APIGatewayAccessLogManager;
import com.api.common.model.entity.GatewayAccessLog;
import com.google.gson.Gson;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 全局风控过滤器（Gateway）
 * 运行逻辑：
 * - 位置：鉴权通过之后、转发下游服务之前；
 * - 动作：提取上下文 → 风控决策 → 写入审计头 / 加严头；必要时 403 拦截；
 * - 高风险但未拦截：标记 `X-Risk-NearBlock:1`，可在前端触发验证码/加强校验；
 * - 审计：把综合分/标签写到响应头（灰度/内网环境可用，线上可采日志）。
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class CustomGlobalFilter implements GlobalFilter, Ordered {

    public static final String ATTR_LAT_RECORDED = "risk.latRecorded";


    private final SlidingWindowStats windowStats;

    private final RiskService riskService;

    private final APIGatewayAccessLogManager apiGatewayAccessLogManager;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();
        String headerBody = headers.getFirst("body");
        if(StringUtils.isBlank(headerBody)){
            log.warn("请求头中缺少 body 参数，跳过风控检测");
            return chain.filter(exchange);
        }
        Gson gson = new Gson();
        Map<String, String> bodyJson ;
        try {
            bodyJson = gson.fromJson(headerBody, Map.class);
        }catch (Exception e){
            log.error("解析请求体失败:{}",e.getMessage());
            return chain.filter(exchange);
        }
        if(bodyJson==null){
            log.warn("请求体为空，跳过风控检测");
            return chain.filter(exchange);
        }

        String interfaceId = bodyJson.get("id");



        // —— 1) 组装 RiskContext（仅示例：真实场景请从你的鉴权/网关上下文读取）——
        RiskContext ctx = new RiskContext();
        ctx.nowMillis = System.currentTimeMillis();
        ctx.ip = request.getHeaders().getFirst("X-Real-IP");
        if (ctx.ip == null) ctx.ip = exchange.getRequest().getRemoteAddress() == null ? null
                : exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
        ctx.ua = request.getHeaders().getFirst("User-Agent");
        ctx.method = request.getMethodValue();
        String rawPath = request.getPath().value();
        ctx.path = FeatureExtractor.normalizePath(rawPath);
        ctx.ak = headers.getFirst("accessKey");
        Object uid =exchange.getAttribute(ApiGlobalFilter.ATTR_USER_ID);//用户id
        if (uid != null)
            try {
            ctx.userId = Long.valueOf(uid.toString());
        } catch (Exception ignored) {
        }

        // 粗略参数统计（Query + 体积）
        String q = request.getURI().getRawQuery();
        ctx.querySizeBytes = q == null ? 0 : q.length();
        ctx.paramCount = q == null ? 0 : Math.max(1, q.split("&").length);
        // Body 大小（若需要精确体尺寸，可在 ReadBodyFilter 中获取后塞到 exchange attribute）
        ctx.bodySizeBytes = bodyJson != null ? headerBody.getBytes(StandardCharsets.UTF_8).length : 0;
        // 头部快照（可选）
        Map<String, String> hs = new HashMap<String, String>();
        request.getHeaders().forEach((k, v) -> hs.put(k, v == null || v.isEmpty() ? "" : v.get(0)));
        ctx.headers = hs;

        // —— 2) 时序计数打点（在风控之前，让本次也纳入滑窗统计）——
        windowStats.mark(ctx.userId, ctx.ak, ctx.ip, ctx.path);

        // —— 3) 风控决策 —— iforest.onnx
        RiskDecision d = riskService.decide(ctx);

        // —— 4) 审计与加严 —— 
        exchange.getResponse().getHeaders().set("X-Risk-Score", String.valueOf(d.score));
        exchange.getResponse().getHeaders().set("X-Risk-Reason", d.reason);
        if (d.nearBlock) {
            // 给前端/下游一个提示：需要二次验证/更严配额
            exchange.getResponse().getHeaders().set("X-Risk-NearBlock", "1");
        }
        GatewayAccessLog gatewayAccessLog = new GatewayAccessLog();
        gatewayAccessLog.setRequestId(exchange.getAttribute(ApiGlobalFilter.ATTR_REQUEST_ID));
        Object cat = exchange.getAttribute(ApiGlobalFilter.ATTR_CREATE_AT);
        if (cat != null) {
            gatewayAccessLog.setCreatedAt(new Date((Long) cat));  // 直接使用时间戳
        }
        gatewayAccessLog.setUserId(ctx.userId);
        gatewayAccessLog.setAk(ctx.ak);
        gatewayAccessLog.setInterfaceInfoId(Long.parseLong(interfaceId));
        gatewayAccessLog.setPath(ctx.path);
        gatewayAccessLog.setMethod(ctx.method);
        gatewayAccessLog.setIp(ctx.ip);
        gatewayAccessLog.setUa(ctx.ua);
        gatewayAccessLog.setHeaderCnt(ctx.headers.size());
        gatewayAccessLog.setParamCnt(ctx.paramCount);
        gatewayAccessLog.setQuerySize(ctx.querySizeBytes);
        gatewayAccessLog.setBodySize(ctx.bodySizeBytes);



        // —— 5) 拦截：超过拦截阈值 ——
        if (!d.allow) {
            exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
            byte[] body = ("{\"code\":403,\"msg\":\"Blocked by risk control\",\"reason\":\"" + d.reason + "\"}")
                    .getBytes(StandardCharsets.UTF_8);

            return exchange.getResponse().writeWith(Mono.just(exchange.getResponse()
                            .bufferFactory().wrap(body)))
                    .doFinally(signalType -> {
                        //避免重复记录
                        if (Boolean.TRUE.equals(exchange.getAttribute(ATTR_LAT_RECORDED))) return;
                        exchange.getAttributes().put(ATTR_LAT_RECORDED, Boolean.TRUE);
                        long start = exchange.getAttribute(ApiGlobalFilter.ATTR_START_NANOS);
                        long costMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
                        if (costMs < 0) costMs = 0;
                        // 写一个响应头方便观察（可选）
                        exchange.getResponse().getHeaders().set("X-Latency-MS", String.valueOf(costMs));

                        gatewayAccessLog.setCode(403);
                        gatewayAccessLog.setSuccess(0);
                        gatewayAccessLog.setLatencyMs((int) costMs);
                        String json = gson.toJson(gatewayAccessLog);
                        apiGatewayAccessLogManager.sendMessage(json);

                    });
        }

        // —— 6) 放行到下游（可能携带 NearBlock 头用于下游加严流程）——
        return chain.filter(exchange)
                .doFinally(signalType -> {
                    //避免重复记录
                    if (Boolean.TRUE.equals(exchange.getAttribute(ATTR_LAT_RECORDED))) return;
                    exchange.getAttributes().put(ATTR_LAT_RECORDED, Boolean.TRUE);

                    long start = exchange.getAttribute(ApiGlobalFilter.ATTR_START_NANOS);
                    long costMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
                    if (costMs < 0) costMs = 0;
                    // 写一个响应头方便观察（可选）
//                    exchange.getResponse().getHeaders().set("X-Latency-MS", String.valueOf(costMs));

                    gatewayAccessLog.setLatencyMs((int) costMs);
                    gatewayAccessLog.setCode(200);
                    gatewayAccessLog.setSuccess(1);
                    String json = gson.toJson(gatewayAccessLog);
                    apiGatewayAccessLogManager.sendMessage(json);

                });
    }

    @Override
    public int getOrder() {
        // 越小越靠前，放在限流/熔断之前或之后按需调整
        return -10;
    }
}